Skip to content

Feat/aggregates#240

Open
jlouvel wants to merge 11 commits intomainfrom
feat/aggregates
Open

Feat/aggregates#240
jlouvel wants to merge 11 commits intomainfrom
feat/aggregates

Conversation

@jlouvel
Copy link
Copy Markdown
Contributor

@jlouvel jlouvel commented Apr 4, 2026

Related Issue

Closes #191


What does this PR do?

Introduces aggregates[] on the Capability object, enabling DDD-inspired domain functions that can be defined once and referenced from multiple adapters (MCP tools, REST operations) via ref.

Schema:

  • New definitions: Aggregate, AggregateFunction, Semantics
  • ref property on McpTool and ExposedOperation (third anyOf branch alongside simple/orchestrated)
  • name and description are optional when using ref (inherited from the function)

Engine:

  • AggregateRefResolver merges inherited fields from the referenced function (name, description, call, with, steps, inputParameters, outputParameters) — explicit fields override inherited ones
  • Automatic derivation of MCP hints from function semantics (safe → readOnly/destructive, idempotent → idempotent), with explicit hints overriding derived values

Spectral:

  • naftiko-aggregate-function-description rule: warns when a function lacks a description
  • naftiko-aggregate-semantics-consistency rule (custom JS function): detects contradictions between function semantics and MCP tool hints (e.g. safe=true + destructive=true) or REST methods (e.g. safe=true + DELETE)

Documentation:

  • Specification-Schema: §3.4.5 (Aggregate Object), §3.5.5 (McpTool ref mode), §3.9 (ExposedOperation ref mode)
  • FAQ: new "Aggregates & Reuse (DDD-inspired)" section
  • design-guidelines, AGENTS.md, SKILL.md, wrap-api-as-mcp: aggregate design rules and constraints

Built on top of #239 (MCP tool hints).


Tests

  • 25 unit tests (AggregateRefResolverTest): function map indexing, ref lookup, MCP tool merge (name, description, call, inputParams), REST operation merge, deriveHints (6 scenarios), mergeHints (3 scenarios), resolve pipeline
  • 11 integration tests (AggregateIntegrationTest): end-to-end YAML → engine → output for ref resolution, parameter inheritance, hints derivation, overrides, backward compatibility, unknown ref fail-fast
  • 3 Spectral tests (NaftikoSpectralRulesetTest): semantics-hints contradiction detection, REST method contradiction, consistent-document no-warn
  • 3 test fixtures: aggregate-basic.yaml, aggregate-hints-override.yaml, aggregate-invalid-ref.yaml, plus 2 Spectral fixtures

Checklist

  • CI is green (build, tests, schema validation, security scans)
  • Rebased on latest main
  • Small and focused — one concern per PR
  • Commit messages follow Conventional Commits

Agent Context (optional)

agent_name: GitHub Copilot
llm: Claude Opus 4.6
tool: VS Code Chat
confidence: high
source_event: "Issue #191 — Factorize capability core with aggregates[]"
discovery_method: code_review
review_focus: >
  AggregateRefResolver.java (merge logic + hints derivation),
  naftiko-schema.json (McpTool/ExposedOperation anyOf branches),
  aggregate-semantics-consistency.js (Spectral custom function)

@jlouvel jlouvel requested a review from eskenazit April 4, 2026 01:04
@jlouvel jlouvel self-assigned this Apr 4, 2026
@jlouvel jlouvel force-pushed the feat/aggregates branch 3 times, most recently from acd2055 to b221b26 Compare April 7, 2026 14:20
"name",
"description"
],
"required": [],
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will not work after merge of mpc mock.

After the rebase of #236 you will have

"required": [],
"anyOf": [
{ "required": ["name", "description", "call"], ... }, // simple
{ "required": ["name", "description", "steps"], ... }, // orchestrated
{ "required": ["ref"] } // ref (aggregate)
]
which will allow an empty tool since ref is required only from an anyOf branching perspective.

I think should become :

{
"required": ["name", "description", "outputParameters"],
"type": "object",
"description": "Mock mode: serves static responses from const values. No call, steps, or ref needed.",
"properties": {
"outputParameters": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/$defs/MockOutputParameter"
}
}
},
"not": {
"anyOf": [
{ "required": ["call"] },
{ "required": ["steps"] },
{ "required": ["ref"] }
]
}
}

But we should close #236 first and rebase since the two re working on the same schema, we will have non trivial conflicts

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's wait for #236 and #239 to merge then :)

jlouvel added 4 commits April 8, 2026 18:22
Replace runtime const injection with a dedicated value field on
MappedOutputParameter. The const field is now schema-only (validation
and linting); value handles static and Mustache-templated output at
runtime.

- Add value field to OutputParameterSpec with serializer/deserializer
- Resolver.resolveOutputMappings resolves Mustache templates in value
- ResourceRestlet mock responses use value with template resolution
- ToolHandler supports mock mode (no call/steps) via value fields
- HttpClientAdapter headers use getValue instead of getConstant
- Update naftiko-schema.json: add value, update oneOf constraints
- Update wiki: FAQ, Use Cases, Schema specification
…consistency

Extract buildMockData and buildMockValue from ResourceRestlet into
Resolver as shared static methods. Both REST (sendMockResponse) and
MCP (buildMockToolResult) now delegate to the same mock builder,
ensuring consistent Mustache resolution and structure handling.
Add mock-capability.md reference story covering static and dynamic
(Mustache) mock output parameters for both MCP and REST adapters.
Update SKILL.md decision table, add Mock workflow section, and add
hard constraints 17-18 for const vs value and Mustache scope rules.
@jlouvel jlouvel force-pushed the feat/aggregates branch 2 times, most recently from 26c18d6 to bdf0a0a Compare April 9, 2026 01:44
…ution

Introduce aggregates[] on Capability for defining reusable domain functions

- Schema: Aggregate, AggregateFunction, Semantics definitions; ref on McpTool and ExposedOperation

- Engine: AggregateRefResolver merges ref fields and derives MCP hints from semantics

- Spectral: aggregate-semantics-consistency rule detects contradictions between semantics, MCP hints, and REST methods

- Tests: 25 unit + 11 integration + 3 Spectral tests

- Docs: Specification-Schema, FAQ, design-guidelines, AGENTS.md, SKILL.md, wrap-api-as-mcp

Closes #191

chore: organize test fixtures into subdirectories

feat: add aggregates with DDD-inspired domain functions and ref resolution

Introduce aggregates[] on Capability for defining reusable domain functions

- Schema: Aggregate, AggregateFunction, Semantics definitions; ref on McpTool and ExposedOperation

- Engine: AggregateRefResolver merges ref fields and derives MCP hints from semantics

- Spectral: aggregate-semantics-consistency rule detects contradictions between semantics, MCP hints, and REST methods

- Tests: 25 unit + 11 integration + 3 Spectral tests

- Docs: Specification-Schema, FAQ, design-guidelines, AGENTS.md, SKILL.md, wrap-api-as-mcp

Closes #191

chore: organize test fixtures into subdirectories
@jlouvel
Copy link
Copy Markdown
Contributor Author

jlouvel commented Apr 9, 2026

@eskenazit I've rebased from #287 , that might work well

jlouvel added 5 commits April 9, 2026 13:39
Replace runtime const injection with a dedicated value field on
MappedOutputParameter. The const field is now schema-only (validation
and linting); value handles static and Mustache-templated output at
runtime.

- Add value field to OutputParameterSpec with serializer/deserializer
- Resolver.resolveOutputMappings resolves Mustache templates in value
- ResourceRestlet mock responses use value with template resolution
- ToolHandler supports mock mode (no call/steps) via value fields
- HttpClientAdapter headers use getValue instead of getConstant
- Update naftiko-schema.json: add value, update oneOf constraints
- Update wiki: FAQ, Use Cases, Schema specification
…consistency

Extract buildMockData and buildMockValue from ResourceRestlet into
Resolver as shared static methods. Both REST (sendMockResponse) and
MCP (buildMockToolResult) now delegate to the same mock builder,
ensuring consistent Mustache resolution and structure handling.
Add mock-capability.md reference story covering static and dynamic
(Mustache) mock output parameters for both MCP and REST adapters.
Update SKILL.md decision table, add Mock workflow section, and add
hard constraints 17-18 for const vs value and Mustache scope rules.
…ution

Introduce aggregates[] on Capability for defining reusable domain functions

- Schema: Aggregate, AggregateFunction, Semantics definitions; ref on McpTool and ExposedOperation

- Engine: AggregateRefResolver merges ref fields and derives MCP hints from semantics

- Spectral: aggregate-semantics-consistency rule detects contradictions between semantics, MCP hints, and REST methods

- Tests: 25 unit + 11 integration + 3 Spectral tests

- Docs: Specification-Schema, FAQ, design-guidelines, AGENTS.md, SKILL.md, wrap-api-as-mcp

Closes #191

chore: organize test fixtures into subdirectories

feat: add aggregates with DDD-inspired domain functions and ref resolution

Introduce aggregates[] on Capability for defining reusable domain functions

- Schema: Aggregate, AggregateFunction, Semantics definitions; ref on McpTool and ExposedOperation

- Engine: AggregateRefResolver merges ref fields and derives MCP hints from semantics

- Spectral: aggregate-semantics-consistency rule detects contradictions between semantics, MCP hints, and REST methods

- Tests: 25 unit + 11 integration + 3 Spectral tests

- Docs: Specification-Schema, FAQ, design-guidelines, AGENTS.md, SKILL.md, wrap-api-as-mcp

Closes #191

chore: organize test fixtures into subdirectories
@jlouvel jlouvel requested a review from eskenazit April 9, 2026 14:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Factorize capability core with aggregates[]: functions first, entities/events later

2 participants